package com.imb.common.web.thread;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.extra.spring.SpringUtil;
import com.imb.common.core.enums.ThreadKeyEnum;
import com.imb.common.web.exception.CustomException;
import com.imb.common.web.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 创建线程池
 */
@Slf4j
@Component
public class ThreadFactorys {

    private AtomicInteger threadNumber = new AtomicInteger(0);

    private String threadName;

    private static String threadKey="thread-key";

    /**
     * 线程池集合
     */
//    private static Set<String> managedExecutors = new ConcurrentHashSet<>();
    private static Map<String,ExecutorService> managedExecutors = new ConcurrentHashMap<>();

    /**
     * 对象锁，防并发写
     */
    private static final Object LOCK = new Object();

    private static final Object LOCK_READ = new Object();

    /**
     * CAS
     */
    private static final AtomicBoolean CLOSED = new AtomicBoolean(false);


    /**
     * 注册线程池
     * @param executorService
     */
    private static void register(String key,ExecutorService executorService) {
        vercheckKey(key);
        synchronized (LOCK) {
            managedExecutors.put(key,executorService);
        }
    }


    /**
     * 获取线程池组集合
     * @return
     */
    public static Map<String,ExecutorService> getManagedExecutors(){
        return managedExecutors;
    }


    public ExecutorService getManagedExecutors(String key){
        ExecutorService service = managedExecutors.get(key);
        synchronized (LOCK_READ){
            if(service==null){
                managedExecutors.remove(key);
                log.info("此[{}]的线程池已被释放了...",key);
                return null;
            }
        }
        return service;
    }

    public  ExecutorService getCreateManagedExecutors(String key){
        ExecutorService service = managedExecutors.get(key);
        synchronized (LOCK_READ){
            if(service==null){
                service = ThreadPoolFactory.getThreadPoolServer(key, 2);
                //注册线程池
                register(key,service);
                log.info("{}线程池已启动--->,{}",key,service);
            }
        }
        return service;
    }


    /**
     * 关闭线程池
     */
    private  void shutdown() {
        // CAS防止多次调用
        if (!CLOSED.compareAndSet(false, true)) {
            return;
        }
//        for (Map.Entry<String, ExecutorService> stringExecutorServiceEntry : managedExecutors.entrySet()) {
//                shutdownThreadPool(stringExecutorServiceEntry.getValue());
//        }
//        for (ExecutorService managedExecutor : managedExecutors) {
//            shutdownThreadPool(managedExecutor);
//        }
    }

    /**
     * 关闭线程池
     * @param executorService 线程池
     */
    public  void shutdownThreadPool(ExecutorService executorService) {
        executorService.shutdown();
        int retry = 3;
        if (retry > 0) {
            retry--;
            try {
                if (executorService.awaitTermination(1, TimeUnit.SECONDS)) {
                    return;
                }
            } catch (InterruptedException e) {
                executorService.shutdownNow();
                Thread.interrupted();
            } catch (Throwable ex) {
                log.error("shutdown thread pool has error: {}", ex);
            }
        }
        executorService.shutdownNow();
    }


    public ExecutorService newThread(String key,int size){
        return newThreadFactory(key,"global-pool",size,size,60L, TimeUnit.SECONDS,size);
    }


    public  ExecutorService newThreadScheduled(String key,int count){
        return add(key,new ScheduledThreadPoolExecutor(count,
                new BasicThreadFactory.
                        Builder().namingPattern("example-schedule-pool-%d").daemon(true).build()));
    }


    public ExecutorService newThreadFactory(String key,String threadName, int coreSize, int maxSize, long aliveTime, TimeUnit timeUnit, int queueSize){
        this.threadName=threadName;
        this.threadNumber=new AtomicInteger(maxSize);
        return add(key,new ThreadPoolExecutor(
                coreSize,
                maxSize,
                aliveTime, timeUnit,
                new LinkedBlockingQueue<>(queueSize),
                new ThreadFactoryConfig(),
                new CustomRejectedExecutionHandler()
        ));
    }

    public  ExecutorService add(String key,ExecutorService executorService){
        register(key,executorService);
        return executorService;
    }

    public static void vercheckKey(String key){
        if(managedExecutors.containsKey(key)){
             throw new CustomException("已存在此key");
        }
    }


    /**
     * 线程工厂
     */
    class ThreadFactoryConfig implements ThreadFactory {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, threadName + "-" + threadNumber.get());
        }
    }

    /**
     * 线程池拒绝策略自定义处理器
     */
    static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.out.println("进入拒绝策略"+r.getClass());
            if (!executor.isShutdown()) {
                try {
                    // 超出队列的任务尝试加入队列
                    //尝试阻塞式加入任务队列
                    // put() : 添加进去，如果队列满了，则等待空出来再添加
                    executor.getQueue().put(r);
                } catch (Exception e) {
                    //保持线程的中断状态(相当于取消这个加入任务)
                    Thread.currentThread().interrupt();
                }
            }
            /**
             * 可以把任务存入缓存，尽量不要丢弃任务
             */
        }
    }

    /**
     * 理想的线程数，使用 2倍cpu核心数
     */
    public static int desiredThreadNum() {
        return Runtime.getRuntime().availableProcessors() * 2;
    }
}
